package com.xx.common.eth;

import com.xx.common.utils.UseEncryptedKey;
import lombok.extern.slf4j.Slf4j;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.*;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
public class Transfer {

    /**
     * Transfers Ether from a sender's account to a recipient's account.
     *
     * @param recipientAddress The recipient's Ethereum address.
     * @param amount           The amount to transfer in Ether.
     * @return The transaction hash if the transfer is successful; otherwise, returns null.
     * @throws IOException If there is an issue communicating with the Ethereum node.
     */
    public static String transfer(String encryptedKey, String secretKey, String rpcUrl, String recipientAddress, String contractAddress, BigDecimal amount) {
        try {
            Web3j web3j = Web3j.build(new HttpService(rpcUrl));
            EthChainId chainIdResponse = web3j.ethChainId().send();
            long chainId = chainIdResponse.getChainId().longValue();

            Credentials credentials = Credentials.create(UseEncryptedKey.decrypt(encryptedKey, secretKey));
            String senderAddress = credentials.getAddress();
            EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(
                    senderAddress, DefaultBlockParameterName.LATEST).send();
            BigInteger nonce = ethGetTransactionCount.getTransactionCount();

            // Convert the amount from tokens to Wei (assumes token has 18 decimals)
            BigInteger value = Convert.toWei(amount, Convert.Unit.ETHER).toBigInteger();

            // Get the current Gas price
            EthGasPrice ethGasPrice = web3j.ethGasPrice().send();
            BigInteger gasPrice = ethGasPrice.getGasPrice();

            // Estimate a gas limit for the token transfer
            BigInteger gasLimit = BigInteger.valueOf(60000); // 通常ERC20转账需要更多的gas

            // Create the transfer transaction data
            String data = createTransferData(recipientAddress, value);

            // Create the transaction object
            RawTransaction rawTransaction = RawTransaction.createTransaction(
                    nonce, gasPrice, gasLimit, contractAddress, data);

            // Sign the transaction
            byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, chainId, credentials);
            String hexValue = Numeric.toHexString(signedMessage);

            // Send the transaction
            EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send();
            // Check if the transaction was successful
            if (ethSendTransaction.hasError()) {
                // Log the error message
                System.err.println("Transaction Error: " + ethSendTransaction.getError().getMessage());
                throw new RuntimeException("Transaction failed: " + ethSendTransaction.getError().getMessage());
            }

            // Get the transaction hash
            String transactionHash = ethSendTransaction.getTransactionHash();

            // Wait for the transaction to be confirmed
            waitForTransactionReceipt(web3j, transactionHash);

            return ethSendTransaction.getTransactionHash();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void waitForTransactionReceipt(Web3j web3j, String transactionHash) {
        try {
            int attempts = 0;
            while (true) {
                EthGetTransactionReceipt transactionReceipt = web3j.ethGetTransactionReceipt(transactionHash).send();
                if (transactionReceipt.getTransactionReceipt().isPresent()) {
                    // Transaction is confirmed
                    System.out.println("Transaction confirmed: " + transactionHash);
                    return;
                }

                // Wait for a bit before checking again
                TimeUnit.SECONDS.sleep(2);
                attempts++;

                // Optionally, you can add a timeout mechanism
                if (attempts >= 30) { // 30 attempts = 60 seconds
                    throw new RuntimeException("Transaction not confirmed after 60 seconds: " + transactionHash);
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("Waiting for transaction receipt interrupted: " + e.getMessage());
        } catch (Exception e) {
            throw new RuntimeException("Error while waiting for transaction receipt: " + e.getMessage());
        }
    }

    // Create the data payload for the transfer function
    private static String createTransferData(String recipientAddress, BigInteger value) {
        String methodSignature = "0xa9059cbb"; // transfer(address,uint256) 的方法签名
        String recipientHex = Numeric.toHexStringNoPrefixZeroPadded(Numeric.toBigInt(recipientAddress), 64);
        String valueHex = Numeric.toHexStringNoPrefixZeroPadded(value, 64);
        return methodSignature + recipientHex + valueHex;
    }

    /**
     * Checks the status of a transaction based on its hash and retrieves transaction details.
     *
     * @param transactionHash The hash of the transaction to check.
     * @return A string describing the transaction status and details.
     * @throws IOException If there is an issue communicating with the Ethereum node.
     */
    public static String getTransactionDetails(String transactionHash) throws IOException {
        String rpcUrl = "https://data-seed-prebsc-1-s1.binance.org:8545";
        Web3j web3j = Web3j.build(new HttpService(rpcUrl));

        // 获取交易回执
        EthGetTransactionReceipt transactionReceipt = web3j.ethGetTransactionReceipt(transactionHash).send();

        if (transactionReceipt.getTransactionReceipt().isPresent()) {
            // 获取交易回执
            org.web3j.protocol.core.methods.response.TransactionReceipt receipt = transactionReceipt.getTransactionReceipt().get();
            String fromAddress = receipt.getFrom();
            String toAddress = receipt.getTo();
            String status = receipt.getStatus();

            // 查询交易信息以获取 value
            EthTransaction ethTransaction = web3j.ethGetTransactionByHash(transactionHash).send();
            if (ethTransaction.getTransaction().isPresent()) {
                BigInteger value = ethTransaction.getTransaction().get().getValue();

                String statusMessage;

                if ("0x1".equals(status)) {
                    statusMessage = "Transaction succeeded.";
                } else if ("0x0".equals(status)) {
                    statusMessage = "Transaction failed.";
                } else {
                    statusMessage = "Transaction status unknown.";
                }

                return String.format("Status: %s\nFrom: %s\nTo: %s\nValue Sent: %s Wei",
                        statusMessage, fromAddress, toAddress, value);
            } else {
                return "Transaction not found.";
            }
        } else {
            return "Transaction not found or still pending.";
        }
    }

    private static final String TRANSFER_METHOD_SIGNATURE = "0xa9059cbb"; // transfer(address,uint256)

    /**
     * Checks the status of a transaction based on its hash and retrieves token transfer details.
     *
     * @param transactionHash The hash of the transaction to check.
     * @return A string describing the transaction status and details.
     * @throws IOException If there is an issue communicating with the Ethereum node.
     */
    public static TransactionStatusVo getTokenTransferDetails(String rpcUrl, String transactionHash) throws IOException {
        Web3j web3j = Web3j.build(new HttpService(rpcUrl));
        // 获取交易回执
        EthGetTransactionReceipt transactionReceipt = web3j.ethGetTransactionReceipt(transactionHash).send();
        if (transactionReceipt.getTransactionReceipt().isPresent()) {
            // 获取交易回执
            org.web3j.protocol.core.methods.response.TransactionReceipt receipt = transactionReceipt.getTransactionReceipt().get();
            String status = receipt.getStatus();
            TransactionStatusVo transactionStatusVo = new TransactionStatusVo();
            // 获取合约地址
            String contractAddress = receipt.getTo();
            if (contractAddress != null) {
                transactionStatusVo.setContractAddress(contractAddress);
            }
            if ("0x0".equals(status)) {
                transactionStatusVo.setStatus(0);
            } else {
                List<Log> logs = receipt.getLogs();
                for (Log log : logs) {
                    // 解析 Transfer 事件
                    String fromAddress = "0x" + log.getTopics().get(1).substring(26); // 从地址
                    String toAddress = "0x" + log.getTopics().get(2).substring(26); // 到地址
                    BigInteger value = new BigInteger(log.getData().substring(2), 16); // 转账金额
                    BigDecimal etherValue = new BigDecimal(value).divide(BigDecimal.TEN.pow(18), MathContext.DECIMAL128);
                    transactionStatusVo.setStatus(1);
                    transactionStatusVo.setFromAddress(fromAddress);
                    transactionStatusVo.setToAddress(toAddress);
                    transactionStatusVo.setAmount(etherValue);
                    String.format("Status: %s\nFrom: %s\nTo: %s\nValue Sent: %s Wei (or %s USDT)",
                            "success", fromAddress, toAddress, value, etherValue);
                }
            }
            return transactionStatusVo;
        } else {
            log.error("Transaction not found or still pending.");
            return null;
        }
    }

}